﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.Collections.Generic;
using ConfigGen.ConsoleApp.Core.CommandLineOptionCore;
using Moq;
using NUnit.Framework;

namespace ConfigGen.ConsoleApp.Core.Tests.CommandLineOptionCore
{
    [TestFixture]
    public class CommandLineOptionExecutorTests
    {
        [Test]
        public void Ctor001_NullArgThrows()
        {
            Assert.Throws<ArgumentNullException>(() => new CommandLineOptionExecutor<TestSettings>(null));
        }

        [Test]
        public void ProcessAndExecuteCommandLineOptions001_ShortForm_NoArgParams()
        {
            var testSettings = new TestSettings();

            var mockRepository = new MockRepository(MockBehavior.Strict);
            var mockHandler = mockRepository.Create<ICommandLineOption<TestSettings>>(MockBehavior.Strict);
            mockHandler.Setup(h => h.ConsumeArgumentsFromQueue(It.IsAny<Queue<string>>()));
            mockHandler.Setup(h => h.ApplyOptionToSettings(testSettings));
            
            var mockArgFactory = mockRepository.Create<ICommandLineOptionFactory<TestSettings>>(MockBehavior.Strict);
            mockArgFactory.Setup(f => f.GetByShortName("t")).Returns(mockHandler.Object);

            var parserUnderTest = new CommandLineOptionExecutor<TestSettings>(mockArgFactory.Object);
            parserUnderTest.ProcessAndExecuteCommandLineOptions(new [] {"-t"}, testSettings);

            mockRepository.VerifyAll();
        }

        [Test]
        public void ProcessAndExecuteCommandLineOptions002_LongForm_NoArgParams()
        {
            var testSettings = new TestSettings();

            var mockRepository = new MockRepository(MockBehavior.Strict);
            var mockHandler = mockRepository.Create<ICommandLineOption<TestSettings>>(MockBehavior.Strict);
            mockHandler.Setup(h => h.ConsumeArgumentsFromQueue(It.IsAny<Queue<string>>()));
            mockHandler.Setup(h => h.ApplyOptionToSettings(testSettings));

            var mockArgFactory = mockRepository.Create<ICommandLineOptionFactory<TestSettings>>(MockBehavior.Strict);
            mockArgFactory.Setup(f => f.GetByLongName("t")).Returns(mockHandler.Object);

            var parserUnderTest = new CommandLineOptionExecutor<TestSettings>(mockArgFactory.Object);
            parserUnderTest.ProcessAndExecuteCommandLineOptions(new[] { "--t" }, testSettings);

            mockRepository.VerifyAll();
        }

        [Test]
        public void ProcessAndExecuteCommandLineOptions003_MultipleArgs()
        {
            var executionOrderList = new List<string>();
            var testSettings = new TestSettings();

            var mockRepository = new MockRepository(MockBehavior.Strict);
            var mockHandlerA = mockRepository.Create<ICommandLineOption<TestSettings>>(MockBehavior.Strict);
            mockHandlerA
                .Setup(h => h.ConsumeArgumentsFromQueue(It.IsAny<Queue<string>>()))
                .Callback(()=>executionOrderList.Add("A-Process"));
            mockHandlerA
                .Setup(h => h.ApplyOptionToSettings(testSettings))
                .Callback(() => executionOrderList.Add("A-ApplyOptionToSettings"));

            var mockHandlerB = mockRepository.Create<ICommandLineOption<TestSettings>>(MockBehavior.Strict);
            mockHandlerB
                .Setup(h => h.ConsumeArgumentsFromQueue(It.IsAny<Queue<string>>()))
                .Callback(() => executionOrderList.Add("B-Process"));
            mockHandlerB
                .Setup(h => h.ApplyOptionToSettings(testSettings))
                .Callback(() => executionOrderList.Add("B-ApplyOptionToSettings"));

            var mockArgFactory = mockRepository.Create<ICommandLineOptionFactory<TestSettings>>(MockBehavior.Strict);
            mockArgFactory.Setup(f => f.GetByShortName("a")).Returns(mockHandlerA.Object);
            mockArgFactory.Setup(f => f.GetByShortName("b")).Returns(mockHandlerB.Object);

            var parserUnderTest = new CommandLineOptionExecutor<TestSettings>(mockArgFactory.Object);
            parserUnderTest.ProcessAndExecuteCommandLineOptions(new[] { "-a", "-b" }, testSettings);

            Assert.AreEqual(4, executionOrderList.Count, "Incorrect number of items in event execution order");

            // verify ConsumeArgumentsFromQueue is called on both handlers first, and then ApplyOptionToSettings on both (always A first then B)
            Assert.AreEqual("A-Process", executionOrderList[0], "Invalid order at item 0");
            Assert.AreEqual("B-Process", executionOrderList[1], "Invalid order at item 1");
            Assert.AreEqual("A-ApplyOptionToSettings", executionOrderList[2], "Invalid order at item 2");
            Assert.AreEqual("B-ApplyOptionToSettings", executionOrderList[3], "Invalid order at item 3");

            mockRepository.VerifyAll();
        }

        [Test]
        public void ProcessAndExecuteCommandLineOptions004_BadArgFormat()
        {
            var testSettings = new TestSettings();
            var mockArgFactory = new Mock<ICommandLineOptionFactory<TestSettings>>(MockBehavior.Strict);

            var parserUnderTest = new CommandLineOptionExecutor<TestSettings>(mockArgFactory.Object);
            Assert.Throws<ArgumentException>(() => parserUnderTest.ProcessAndExecuteCommandLineOptions(new[] {"badformat"}, testSettings));

            mockArgFactory.VerifyAll();
        }
    }
}
